package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.constant.PromotionConstants;
import com.example.dto.CouponFormDTO;
import com.example.dto.CouponIssueFormDTO;
import com.example.entity.Coupon;
import com.example.entity.UserCoupon;
import com.example.entity.enums.CouponGetEnum;
import com.example.entity.enums.CouponStatusEnum;
import com.example.entity.enums.CouponTypeEnum;
import com.example.entity.enums.UserCouponStatusEnum;
import com.example.mapper.CouponMapper;
import com.example.service.CouponService;
import com.example.service.StoreService;
import com.example.service.UserCouponService;
import com.example.strategy.Discount;
import com.example.strategy.DiscountStrategy;
import com.example.util.BeanCopyPropertiesUtils;
import com.example.util.BeanUtils;
import com.example.util.RedisUtil;
import com.example.vo.CouponVO;
import com.example.vo.LoginUser;
import com.example.vo.Result;
import com.example.vo.ResultCode;
import com.example.vo.buyer.CartGroupedByStoreVo;
import com.example.vo.buyer.CartItemVo;
import com.example.vo.buyer.DiscountDetailVo;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.entity.enums.CouponStatusEnum.NOT_ISSUED;
import static com.example.entity.enums.CouponStatusEnum.ONGOING;

/**
* @author circle48
*/
@Service
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon>
    implements CouponService {

    @Resource
    private UserCouponService userCouponService;

    @Autowired
    private RedisUtil redisUtil;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private StoreService storeService;

    /**
     * 查询可以领取的优惠券
     *
     * @param pageNum
     * @param pageSize
     * @return
     */
    @Override
    public Result<IPage<CouponVO>> getCouponList(Integer pageNum, Integer pageSize) {
        //1. 查询所有符合条件的优惠券列表
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, ONGOING)
                .eq(Coupon::getCouponType, CouponGetEnum.FREE)
                .list();

        if (couponList.isEmpty()) {
            return Result.ok(null, "暂无可领取优惠券");
        }

        // 2. 查询当前用户
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long userId = loginUser.getUser().getId();
        // 3. 查询当前用户已经领取的优惠券
        List<UserCoupon> userCouponList = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .in(UserCoupon::getCouponId, couponList.stream().map(Coupon::getId).collect(Collectors.toList()))
                .list();

        // 4. 构建当前用户已经领取的优惠券映射
        Map<Long, Long> issuedMap = userCouponList.stream()
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        // 5. 把 List<UserCoupon> 转成 Map , 当前用户已经领取当前优惠券的数量(并且没有使用);
        Map<Long, Long> unusedMap = userCouponList.stream()
                .filter(uc -> uc.getStatus() == UserCouponStatusEnum.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        // 6. 遍历 List<Coupon>， 封装 VO，过滤已领取且已使用和已过期的优惠券
        List<CouponVO> voList = new ArrayList<>();
        for (Coupon coupon : couponList) {
            // 如果领取限制为1并且用户已经领取且状态为已使用或已过期，跳过此优惠券
            if (coupon.getLimitReceiveCount() == 1
                    && issuedMap.getOrDefault(coupon.getId(), 0L) > 0
                    && userCouponList.stream()
                    .anyMatch(uc -> uc.getCouponId().equals(coupon.getId())
                            && (uc.getStatus() == UserCouponStatusEnum.USED || uc.getStatus() == UserCouponStatusEnum.EXPIRED))) {
                continue; // 跳过已领取且已使用和已过期的优惠券
            }


            // 每遍历一次, 就要封装成一个 vo
            CouponVO vo = BeanCopyPropertiesUtils.copyBean(coupon, CouponVO.class);

            // 6.1 available 是否可领取: receiveCount < publishCount && 当前用户已经领取当前优惠券的数量 < limitReceiveCount
            vo.setAvailable(coupon.getReceiveCount() < coupon.getPublishCount()
                    && issuedMap.getOrDefault(coupon.getId(), 0L) < coupon.getLimitReceiveCount());

            // 6.2 received 是否已经领取(可以使用): 当前用户已经领取当前优惠券的数量 > 0
            vo.setReceived(unusedMap.getOrDefault(coupon.getId(), 0L) > 0);

            voList.add(vo);
        }

        // 7. 根据分页参数进行分页处理
        int totalSize = voList.size();
        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, totalSize);
        List<CouponVO> currentPageData = voList.subList(startIndex, endIndex);

        // 8. 将结果封装成分页结果返回
        IPage<CouponVO> resultPage = new Page<>(pageNum, pageSize);
        resultPage.setRecords(currentPageData);
        resultPage.setTotal(totalSize);
        return Result.ok(resultPage);
    }


    @Override
    public boolean saveDiscountDetails(Long userId, List<DiscountDetailVo> discountDetails) {
        String redisKey = "user:" + userId + ":cart";
        Object cachedCart = redisUtil.get(redisKey);
        if (cachedCart instanceof List) {
            List<CartGroupedByStoreVo> cartGroupedByStoreVos = (List<CartGroupedByStoreVo>) cachedCart;
            for (DiscountDetailVo detailVo : discountDetails) {
                for (CartGroupedByStoreVo cartGroup : cartGroupedByStoreVos) {
                    if (cartGroup != null && cartGroup.getItems() != null) {
                        for (CartItemVo cartItem : cartGroup.getItems()) {
                            if (cartItem.getSkuId().equals(detailVo.getItemId())) {
                                cartItem.setCouponAmount(detailVo.getDiscountAmount());
                                cartItem.setRealAmount(detailVo.getFinalPrice());
                                cartItem.setCouponIds(detailVo.getCouponIds());
                            }
                        }
                    }
                }
            }
            redisUtil.set(redisKey, cartGroupedByStoreVos);
            return true;
        }
        return false;
    }

    @Override
    public boolean clearDiscountDetails(Long userId){
        String redisKey = "user:" + userId + ":cart";
        Object cachedCart = redisUtil.get(redisKey);
        if (Objects.equals(cachedCart, null) || (cachedCart instanceof List && ((List<?>) cachedCart).isEmpty())) {
            return true;
        }

        if (cachedCart instanceof List) {
            List<CartGroupedByStoreVo> cartGroupedByStoreVos = (List<CartGroupedByStoreVo>) cachedCart;
            for (CartGroupedByStoreVo cartGroup : cartGroupedByStoreVos) {
                for (CartItemVo cartItem : cartGroup.getItems()) {
                    cartItem.setCouponAmount(null);
                    cartItem.setRealAmount(null);
                }
            }
            redisUtil.set(redisKey, cartGroupedByStoreVos);
            return true;
        }
        return false;
    }


    @Override
    public Result issueCoupon(CouponIssueFormDTO dto) {
        log.info("issueCoupon--{}", Thread.currentThread().getId());

        // 1. 根据 id 查询优惠券
        Coupon old = getById(dto.getId());
        if (old == null) {
            return Result.error(ResultCode.COUPON_NOT_FOUND);
        }

        // 2. 判断优惠券的状态
        if (old.getStatus() != NOT_ISSUED) {
            return Result.error(ResultCode.COUPON_STATUS_INCORRECT);
        }

        // 3. 更新优惠券
        // 3.1 拷贝属性
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);

        // 3.2 设置状态
        LocalDateTime now = LocalDateTime.now();
        if (coupon.getEnableStartTime().isBefore(now) || coupon.getEnableStartTime().isEqual(now)) {
            coupon.setStatus(CouponStatusEnum.ONGOING);
            // 3.3 写入数据库并缓存到 Redis
            updateById(coupon);
            cacheCouponInfo(coupon);
            System.out.println("写入redis");
        } else {
            coupon.setStatus(CouponStatusEnum.PENDING);
            // 仅写入数据库
            updateById(coupon);
        }

        return Result.ok();
    }

    /**
     * 缓存优惠券信息
     * @param coupon
     */
    private void cacheCouponInfo(Coupon coupon) {
        String key = PromotionConstants.COUPON_CACHE_KEY_PREFIX+coupon.getId();

        HashMap<String, String> map = new HashMap<>();
        map.put("enableStartTime",coupon.getEnableStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        map.put("enableEndTime",coupon.getEnableEndTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        map.put("publishCount",String.valueOf(coupon.getPublishCount()));
        map.put("limitReceiveCount",String.valueOf(coupon.getLimitReceiveCount()));
        map.put("receiveCount",String.valueOf(0));
        stringRedisTemplate.opsForHash().putAll(key,map);

    }

    @Override
    public void updateUseCount(Long couponId) {
        baseMapper.updateUseCount(couponId);
    }


    @Override
    public Result addCoupon(CouponFormDTO dto) {

        // 1. 根据店铺ID查询店铺名称
        if (dto.getStoreId() == 0) {
            dto.setStoreName("平台");
        } else {
            String storeName = storeService.getStoreNameById(dto.getStoreId());
            if (storeName == null) {
                return Result.error("店铺不存在");
            }
            dto.setStoreName(storeName);
        }


        // 2. 转成Coupon
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);

        // 3. 检查优惠券类型并设置minPoint为0如果是无门槛优惠券
        if (dto.getCouponCategory() == CouponTypeEnum.NO_THRESHOLD) {
            coupon.setMinPoint(0);
        }
        // 4. 生成优惠券描述
        Discount discount = DiscountStrategy.getDiscount(coupon.getCouponCategory());
        String ruleDescription = discount.getRule(coupon);
        coupon.setDescription(ruleDescription);
        coupon.setUseCount(0);
        coupon.setReceiveCount(0);

        coupon.setStatus(NOT_ISSUED);
        // 4. 保存Coupon(1条记录)
        save(coupon);

        return Result.ok();
    }

    @Override
    public Result<IPage<Coupon>> queryPage(Integer pageNum, Integer pageSize) {
        Page<Coupon> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Coupon> queryWrapper = new QueryWrapper<>();

        IPage<Coupon> couponPage = this.page(page, queryWrapper);

        return Result.ok(couponPage);
    }




    @Override
    public void updateStatusToStart(LocalDateTime currentTime) {
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatusEnum.PENDING)
                .ge(Coupon::getEnableStartTime, currentTime)
                .list();

        couponList.forEach(coupon -> {
            coupon.setStatus(ONGOING);
            updateById(coupon);
            cacheCouponInfo(coupon);
        });
    }
    @Override
    public void updateStatusToPending(LocalDateTime currentTime) {
        lambdaUpdate()
                .set(Coupon::getStatus, CouponStatusEnum.PENDING)
                .eq(Coupon::getStatus, CouponStatusEnum.NOT_ISSUED)
                .gt(Coupon::getEnableStartTime, currentTime)
                .update();
    }
    @Override
    public void updateStatusToEnded(LocalDateTime currentTime) {
        // 查询领取结束时间在当前时间之前的优惠券
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatusEnum.ONGOING)
                .le(Coupon::getEndTime, currentTime)
                .list();

        couponList.forEach(coupon -> {
            coupon.setStatus(CouponStatusEnum.ENDED);
            updateById(coupon);
        });
    }

    @Override
    public void updateStatusToExpired(LocalDateTime currentTime) {
        // 查询使用结束时间在当前时间之前的优惠券
        List<Coupon> couponList = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatusEnum.ONGOING)
                .le(Coupon::getEnableEndTime, currentTime)
                .list();

        couponList.forEach(coupon -> {
            coupon.setStatus(CouponStatusEnum.EXPIRED);
            updateById(coupon);
        });
    }



    @Override
    public Result<IPage<Coupon>> queryMyStorePage(Integer pageNum, Integer pageSize, Long storeId,
                                                  Integer couponCategory, Integer couponStatus, String couponName) {
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Coupon::getStoreId, storeId);

        // 添加优惠券类型和状态的查询条件
        if (couponCategory != null) {
            queryWrapper.eq(Coupon::getCouponCategory, couponCategory);
        }
        if (couponStatus != null) {
            queryWrapper.eq(Coupon::getStatus, couponStatus);
        }
        // 添加优惠券名称的模糊查询条件
        if (couponName != null && !couponName.isEmpty()) {
            queryWrapper.like(Coupon::getCouponName, couponName);
        }

        // 分页查询优惠券
        Page<Coupon> page = new Page<>(pageNum, pageSize);
        IPage<Coupon> couponPage = baseMapper.selectPage(page, queryWrapper);

        return Result.ok(couponPage);
    }

    @Override
    public Result<IPage<Coupon>> getAllCouponList(Integer pageNum, Integer pageSize, Integer couponCategory, Integer couponStatus, String couponName) {
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();

        // 1.添加优惠券类型和状态的查询条件
        if (couponCategory != null) {
            queryWrapper.eq(Coupon::getCouponCategory, couponCategory);
        }
        if (couponStatus != null) {
            queryWrapper.eq(Coupon::getStatus, couponStatus);
        }
        // 添加优惠券名称的模糊查询条件
        if (couponName != null && !couponName.isEmpty()) {
            queryWrapper.like(Coupon::getCouponName, couponName);
        }

        // 分页查询优惠券
        Page<Coupon> page = new Page<>(pageNum, pageSize);
        IPage<Coupon> couponPage = baseMapper.selectPage(page, queryWrapper);

        return Result.ok(couponPage);
    }

    @Override
    public List<Coupon> getListByIds(List<Long> couponIds) {
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.in(Coupon::getId, couponIds);

        return baseMapper.selectList(queryWrapper);
    }




}